#ifndef SYLAR_SCHEDULER_H
#define SYLAR_SCHEDULER_H
#include <memory>
#include "fiber.h"
#include <functional>
#include "thread.h"
#include "log.h"
#include <vector>
#include <list>
namespace sylar
{
    class Scheduler
    {
    public:
        typedef std::shared_ptr<Scheduler> ptr;
        typedef Mutex MutexType;
        Scheduler(size_t thread_num = 1, bool use_caller = true, const std::string &name = "Srne");
        virtual ~Scheduler();
        const std::string &getName() const { return m_name; }
        static Scheduler *GetThis();
        static Fiber *GetMainFiber();
        void start();
        void stop();
        template <class FiberCb>
        void schedule(FiberCb fc, int64_t thId = -1)
        {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                need_tickle = scheduleNoLock(fc, thId);
            }
            if (need_tickle)
            {
                tickle();
            }
        }
        template <class InputIterator>
        void schedule(InputIterator begin, InputIterator end)
        {
            bool need_tickle = false;
            {
                MutexType::Lock lock(m_mutex);
                while (begin != end)
                {
                    need_tickle = scheduleNoLock(&*begin, -1) || need_tickle;
                    begin++;
                }
            }
            if (need_tickle)
            {
                tickle();
            }
        }

    private:
        template <class FiberCb>
        bool scheduleNoLock(FiberCb fc, int64_t thId = -1)
        {
            bool need_tickle = m_fiberOrTasks.empty();
            FiberOrTask ft(fc, thId);
            if (ft.fiber || ft.cb)
            {
                m_fiberOrTasks.push_back(ft);
            }
            return need_tickle;
        }

    private:
        struct FiberOrTask
        {
            Fiber::ptr fiber;
            std::function<void()> cb;
            int64_t threadId;
            FiberOrTask(Fiber::ptr fib, int64_t thId) : fiber(fib), threadId(thId) {}
            FiberOrTask(Fiber::ptr *fib, int64_t thId) : threadId(thId)
            {
                fiber.swap(*fib);
            }
            FiberOrTask(std::function<void()> f, int64_t thId) : cb(f), threadId(thId)
            {
            }
            FiberOrTask(std::function<void()> *f, int64_t thId) : threadId(thId)
            {
                cb.swap(*f);
            }
            FiberOrTask() : threadId(-1) {}
            void reset()
            {
                fiber = nullptr;
                cb = nullptr;
                threadId = -1;
            }
        };

    protected:
        void run();

        virtual void tickle();
        virtual bool stopping();
        void setThis();
        virtual void idle();

    private:
        mutable MutexType m_mutex;
        std::vector<Thread::ptr> m_threads;
        std::list<FiberOrTask> m_fiberOrTasks;
        size_t m_threadNum;
        std::string m_name;
        Fiber::ptr m_rootFiber;

    protected:
        std::vector<int> m_threadIds;
        std::atomic<size_t> m_threadCount{0};
        std::atomic<size_t> m_activeThreadCount{0};
        std::atomic<size_t> m_idleThreadCount{0};
        bool m_stopping = false;
        bool m_autoStop = false;
        int m_rootThread = -1;
    };
}

#endif
